#pragma once

#define EX_R(condition, text) if(condition) { throw std::runtime_error(text); }
#define EX_L(condition, text) if(condition) { throw std::logic_error(text); }
#define FOR_VEC_I(iter, vec, type) for(std::vector<type>::iterator iter = vec.begin(); iter != vec.end(); iter++)
#define ROUNDH(arg) (floor((float)arg/100.0f)*100)
#define WR(text) std::cout << text << std::endl;

class C_Size
{
private:
	UINT32 m_width;
	UINT32 m_height;
public:
	void setWidth(UINT32 w) { m_width = w; }
	void setHeight(UINT32 h) { m_height = h; }

	UINT32 width() { return m_width; }
	UINT32 height() { return m_height; }
};

typedef struct _Pointi8
{
	INT8 x;
	INT8 y;
	static _Pointi8 create(INT8 x, INT8 y)
	{
		_Pointi8 result;
		result.x = x;
		result.y = y;
		return result;
	}

	static _Pointi8* createp(INT8 x, INT8 y)
	{
		_Pointi8* result = new _Pointi8();
		result->x = x;
		result->y = y;
		return result;
	}
} S_Pointi8;

typedef struct _Pointi16
{
	INT16 x;
	INT16 y;
	static _Pointi16 create(INT16 x, INT16 y)
	{
		_Pointi16 result;
		result.x = x;
		result.y = y;
		return result;
	}

	static _Pointi16* createp(INT16 x, INT16 y)
	{
		_Pointi16* result = new _Pointi16();
		result->x = x;
		result->y = y;
		return result;
	}
} S_Pointi16;

typedef struct _MapPoint
{
	int iX;		//int
	int iY;		//int
	float rX;	//relative x
	float rY;	//relative y

	static _MapPoint make(int _iX, int _iY, float _rX, float _rY)
	{
		_MapPoint res;
		res.iX = _iX;
		res.iY = _iY;
		res.rX = _rX;
		res.rY = _rY;
		return res;
	}
} S_MapPoint;

typedef struct _Pointi
{
	INT64 x;
	INT64 y;
	static _Pointi create(INT64 x, INT64 y)
	{
		_Pointi result;
		result.x = x;
		result.y = y;
		return result;
	}

	static _Pointi* createp(INT64 x, INT64 y)
	{
		_Pointi* result = new _Pointi();
		result->x = x;
		result->y = y;
		return result;
	}

	bool operator==(_Pointi &v)
	{
		return x == v.x && y == v.y;
	}
} S_Pointi;

typedef struct _Pointf
{
	float x;
	float y;
	static _Pointf create(float x, float y)
	{
		_Pointf result;
		result.x = x;
		result.y = y;
		return result;
	}

	static _Pointf* createp(float x, float y)
	{
		_Pointf* result = new _Pointf();
		result->x = x;
		result->y = y;
		return result;
	}
} S_Pointf;

typedef struct _Sizei
{
	S_Pointi min;
	S_Pointi max;

	static _Sizei create(INT64 xmin = 0, INT64 ymin = 0, INT64 xmax = 1, INT64 ymax = 1)
	{
		_Sizei s;
		s.min.x = xmin;
		s.min.y = ymin;

		s.max.x = xmax;
		s.max.y = ymax;
		return s;
	}

	static _Sizei* createp(INT64 xmin = 0, INT64 ymin = 0, INT64 xmax = 1, INT64 ymax = 1)
	{
		_Sizei *s = new _Sizei();
		s->min.x = xmin;
		s->min.y = ymin;

		s->max.x = xmax;
		s->max.y = ymax;
		return s;
	}
} S_Sizei;

typedef struct _Space
{
	float m_cellSize;

	S_Pointi m_globalMin;
	S_Pointi m_globalMax;

	S_Pointi m_center; 	//coords
	S_Pointi m_min;
	S_Pointi m_max;
	S_Pointi m_size;
	S_Pointi m_screen;

	static _Space create(INT64 x = 0, INT64 y = 0, float cellSize = 50.0f)
	{
		_Space s;
		s.m_cellSize = cellSize;

		//center
		s.m_center.x = x;
		s.m_center.y = y;

		//screen size
		INT64 width = ceil((float)WINDOW_WIDTH / s.m_cellSize) + 2;
		INT64 height = ceil((float)WINDOW_HEIGHT / s.m_cellSize) + 2;

		//screen size
		s.m_screen.x = width;
		s.m_screen.y = height;

		//min
		s.m_min.x = s.m_center.x - width;
		s.m_min.y = s.m_center.y - height;

		//max
		s.m_max.x = s.m_center.x + width;
		s.m_max.y = s.m_center.y + height;

		//size
		s.m_size.x = s.m_max.x - s.m_min.x;
		s.m_size.y = s.m_max.y - s.m_min.y;

		//set global size
		s.m_globalMin = s.m_min;
		s.m_globalMax = s.m_max;
		return s;
	}

	void print()
	{
		WR("m_globalMax: " << m_globalMax.x << "; " << m_globalMax.y);
		WR("m_globalMin: " << m_globalMin.x << "; " << m_globalMin.y);
		WR("m_center: " << m_center.x << "; " << m_center.y);
		//WR("m_min: " << m_min.x << "; " << m_min.y);
		//WR("m_max: " << m_max.x << "; " << m_max.y);
	}

	void move(INT64 x, INT64 y)
	{
		m_min.x += x;
		m_min.y += y;

		m_max.x += x;
		m_max.y += y;

		m_center.x += x;
		m_center.y += y;

		m_size.x = m_max.x - m_min.x;
		m_size.y = m_max.y - m_min.y;

		if (m_globalMax.x < m_max.x) m_globalMax.x = m_max.x;
		if (m_globalMax.y < m_max.y) m_globalMax.y = m_max.y;
		if (m_globalMin.x > m_min.x) m_globalMin.x = m_min.x;
		if (m_globalMin.y > m_min.y) m_globalMin.y = m_min.y;
	}

	void zoom(float z)
	{
		if (m_cellSize + z <= 25.0f)
		{
			m_cellSize = 25.0f;
		}
		//else if (m_cellSize + z > WINDOW_WIDTH || m_cellSize + z > WINDOW_HEIGHT)
		//{
		//	m_cellSize = (m_cellSize + z >+ WINDOW_WIDTH) ? WINDOW_WIDTH : WINDOW_HEIGHT;
		//}
		else
		{
			m_cellSize += z;
		}
	}
} S_Space;

typedef struct _Rangei
{
	int min;
	int max;

	static _Rangei make(int _min, int _max)
	{
		_Rangei range;
		range.min = _min;
		range.max = _max;
		return range;
	}
	static _Rangei* makep(int _min, int _max)
	{
		_Rangei *range = new _Rangei();
		range->min = _min;
		range->max = _max;
		return range;
	}
} S_Rangei;
#define RANGEi(min, max) S_Rangei::make(min, max)

typedef struct _Rangef
{
	float min;
	float max;

	static _Rangef make(float _min, float _max)
	{
		_Rangef range;
		range.min = _min;
		range.max = _max;
		return range;
	}
	static _Rangef* makep(float _min, float _max)
	{
		_Rangef *range = new _Rangef();
		range->min = _min;
		range->max = _max;
		return range;
	}
} S_Rangef;
#define RANGEf(min, max) S_Rangef::make(min, max)

enum E_StellarClass
{
	SC_O = 0,
	SC_B = 1,
	SC_A = 2,
	SC_F = 3,
	SC_G = 4,
	SC_K = 5,
	SC_M = 6,
	SC_L = 7,
	SC_T = 8,
	SC_Y = 9
};

typedef struct _StellarClass
{
	E_StellarClass	type;
	S_Rangei		temp;
	S_Rangef		mass;
	S_Rangef		radius;
	S_Rangef		luminosity;

	static _StellarClass make(
		E_StellarClass _type,
		S_Rangei _temp,
		S_Rangef _mass,
		S_Rangef _radius,
		S_Rangef _luminosity
	)
	{
		_StellarClass res;
		res.type		= _type;
		res.temp		= _temp;
		res.mass		= _mass;
		res.radius		= _radius;
		res.luminosity	= _luminosity;
		return res;
	}
} S_StellarClass;
#define STELLAR_CLASS(type, temp, mass, radius, luminosity) S_StellarClass::make(type, temp, mass, radius, luminosity)

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////		STELLAR CLASSES
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///		NAME								TYPE  TEMPERATURE				MASS					RADIUS					LUMINOSITY			
#define STELLAR_CLASS_O		STELLAR_CLASS(	SC_O, RANGEi(30000, 60000),		RANGEf(16.0f, 60.0f),	RANGEf(7.0f, 15.0f),	RANGEf(30000.f, 1400000.f)		)
#define STELLAR_CLASS_B		STELLAR_CLASS(	SC_B, RANGEi(10000, 30000),		RANGEf(2.1f, 16.f),		RANGEf(1.8f, 6.6f),		RANGEf(25.f, 30000.f)			)
#define STELLAR_CLASS_A		STELLAR_CLASS(	SC_A, RANGEi(7500, 10000),		RANGEf(1.4f, 2.1f),		RANGEf(1.4f, 1.8f),		RANGEf(5.0f, 25.f)				)
#define STELLAR_CLASS_F		STELLAR_CLASS(	SC_F, RANGEi(6000, 7500),		RANGEf(1.04f, 1.4f),	RANGEf(1.15f, 1.4f),	RANGEf(1.5f, 5.f)				)
#define STELLAR_CLASS_G		STELLAR_CLASS(	SC_G, RANGEi(5200, 6000),		RANGEf(0.8f, 1.04f),	RANGEf(0.96f, 1.15f),	RANGEf(0.6f, 1.5f)				)
#define STELLAR_CLASS_K		STELLAR_CLASS(	SC_K, RANGEi(3700, 5200),		RANGEf(0.45f, 0.8f),	RANGEf(0.7f, 0.96f),	RANGEf(0.08f, 0.6f)				)
#define STELLAR_CLASS_M		STELLAR_CLASS(	SC_M, RANGEi(2400, 3700),		RANGEf(0.08f, 0.45f),	RANGEf(0.15f, 0.7f),	RANGEf(0.001f, 0.08f)			)
#define STELLAR_CLASS_L		STELLAR_CLASS(	SC_L, RANGEi(1300, 2400),		RANGEf(0.001f, 0.07f),	RANGEf(0.08f, 0.15f),	RANGEf(0.00005f, 0.001f)		)
#define STELLAR_CLASS_T		STELLAR_CLASS(	SC_T, RANGEi(500, 1300),		RANGEf(0.001f, 0.07f),	RANGEf(0.08f, 0.14f),	RANGEf(0.000001f, 0.00005f)		)
#define STELLAR_CLASS_Y		STELLAR_CLASS(	SC_Y, RANGEi(400, 500),			RANGEf(0.0005f, 0.02f),	RANGEf(0.08f, 0.14f),	RANGEf(0.0000001f, 0.000001f)	)

inline S_StellarClass getStellarClass(float index)
{
		if(index >= 98.0f && index < 100.0f) return STELLAR_CLASS_O;	//98% - 100.0f
		if(index >= 95.0f && index < 98.0f) return STELLAR_CLASS_B;		//95.0% - 98.0%
		if(index >= 90.0f && index < 95.0f) return STELLAR_CLASS_A;		//90% - 95%
		if(index >= 80.0f && index < 90.0f) return STELLAR_CLASS_F;		//80.0% - 90.0%
		if(index >= 65.0f && index < 80.0f) return STELLAR_CLASS_G;		//65.0f - 80.0%
		if(index >= 40.0f && index < 65.0f) return STELLAR_CLASS_K;		//40.0% - 65.0%
		if(index >= 1.0f && index < 40.0f) return STELLAR_CLASS_M;		//1.0f% - 40.0%
		if(index >= 0.5f && index < 1.0f) return STELLAR_CLASS_L;		//0.5% - 1.0%
		if(index >= 0.2f && index < 0.5f) return STELLAR_CLASS_T;		//0.2% - 0.5%
		if(index >= 0.0f && index < 0.2f) return STELLAR_CLASS_Y;		//0.0 - 0.2%
	return STELLAR_CLASS_G;
}

#define IMG_SC_Y 0,0, 492, 492

typedef struct _MousePressMove
{
	bool isPress;
	int x;
	int y;
	int lastX;
	int lastY;

	static _MousePressMove make(int _x = 0, int _y = 0)
	{
		_MousePressMove res;
		res.x = _x;
		res.y = _y;
		res.isPress = false;
		return res;
	}
	static _MousePressMove* makep(int _x = 0, int _y = 0)
	{
		_MousePressMove *res = new _MousePressMove();
		res->x = _x;
		res->y = _y;
		res->isPress = false;
		return res;
	}
	void onPress(int _x, int _y) { if (!isPress) { lastX = _x; lastY = _y; isPress = true; } }
	void onRelease() { isPress = false; }
	void move(int _x, int _y) { this->x = _x - this->lastX; this->y = _y - this->lastY; this->lastX = _x; this->lastY = _y; }
} S_MousePressMove;